QT插件学习系列(三) 插件间通信 您所在的位置:网站首页 qt 消息传递 QT插件学习系列(三) 插件间通信

QT插件学习系列(三) 插件间通信

2023-09-17 10:03| 来源: 网络整理| 查看: 265

1、 概述

前两篇文章学完我们可以从0开始写一个 QT 插件了,也有自己的简易版插件框架了。但是例子中只给出了单个插件的加载与调用,我们实现插件化的目的是为了扩展性,实际项目中会存在插件A,插件B,插件C。而这些插件之间怎么通信 ? 或者说怎么建立一种良好的通信结构,这既是一项必不可少的工作,更是完善我们插件框架的重要里程碑。

在我们实现通信机制的时候,要考虑下面几点(不止在这儿,在任何时候都要这么做):

1、 耦合性要低。 2、 代码量要少。 3、 扩展性要强。 4、 封装、继承、多态。

我们要尽量符合面向对象原则,面向对象中,再面向接口编程,而不是面向实现。

通信包括插件和主程序之间的通信,也包括插件和插件之间的通信,首先,在插件里面是获取不到主程序的内容的(试想键盘能知道 windows 内核的东西吗,它们之间是主从设备的关系,即插件管理器和插件也是主从关系),其次插件 A 也不知道插件 B 的存在(键盘不知道鼠标的存在),即无依赖关系的插件之间 0 耦合。

下图展示了我们整个插件的通信机制: 插件通信

本想建立一个单例的消息中转站 Router,用于转发消息的,但是一想算了,既然是个 Demo 就简单点,直接用 PluginManager 类吧。

2、 实例

基于文章(二)2019-05-28 QT插件学习系列(二) 插件管理器 的实例,我们增加一个模拟场景,主界面有个按钮,点击打开插件 A 的界面,插件 A 上有个按钮,可以通过点击调用插件 B 的动画方法。

image

# pluginMetaData.h #pragma once #include #include #include #include /** * 插件通信消息封装体 **/ struct PluginMetaData { QString from; QString dest; int type; QMap map; QObject *object = nullptr; }; Q_DECLARE_METATYPE(PluginMetaData);

首先需要封装一个标准的通信数据包,便于统一方法参数。

#pragma once #include #include #include #include "pluginMetaData.h" //定义接口 class InterfacePlugin { public: virtual ~InterfacePlugin() {} virtual void recMsgfromManager(PluginMetaData) = 0; virtual void sendMsg2Manager(PluginMetaData) = 0; }; #define InterfacePlugin_iid "Test.Plugin.InterfacePlugin" Q_DECLARE_INTERFACE(InterfacePlugin, InterfacePlugin_iid)

针对接口进行了改进,增加了两个虚函数,一个是接收 manager 的消息,一个是发送消息给 manager 让其进行转发。

# qtpluginmanager.cpp void QtPluginsManager::initSignalAndSlot() { auto plugins = allPlugins(); foreach (auto loader, plugins) { // 每个插件发送消息到manager,然后由manager 根据 dest 字段转发 connect(loader->instance(),SIGNAL(sendMsg2Manager(PluginMetaData)),this,SLOT(recMsgfromPlugin(PluginMetaData))); } } void QtPluginsManager::recMsgfromPlugin(PluginMetaData metadata) { auto loader = getPlugin(metadata.dest); if(loader) { auto interface = qobject_cast(loader->instance());; if(interface) { interface->recMsgfromManager(metadata); } } }

pluginmanager 类也增加了两个方法,一个是在加载完所有插件后初始化和插件通信的信号槽,一个是收到插件发来的消息后根据 dest 字段转发给对应的插件。

# widget.cpp #include "widget.h" #include "ui_widget.h" #include "qtpluginmanager.h" #include "interfaceplugin.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); connect(ui->pushButtonA,SIGNAL(clicked()),this,SLOT(clickA())); } Widget::~Widget() { delete ui; } void Widget::clickA() { QPluginLoader *loader = QtPluginsManager::getInstance().getPlugin("pluginA"); if(loader) { auto obj = loader->instance(); if(obj->isWidgetType()) { QWidget *widget = qobject_cast(obj); widget->show(); } } else { qDebug() setupUi(this); connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(clickOpenImg())); connect(ui->pushButton2,SIGNAL(clicked()),this,SLOT(click2Animation())); } void PluginA::clickOpenImg() { QString path = QFileDialog::getOpenFileName(this, tr("选择图片"), ".", tr("Image Files(*.jpg *.png)")); ui->label->setPixmap(QPixmap(path)); } void PluginA::click2Animation() { PluginMetaData metadata; metadata.from = "pluginA"; metadata.dest = "pluginB"; metadata.type = 1; metadata.object = ui->label; emit sendMsg2Manager(metadata); } void PluginA::recMsgfromManager(PluginMetaData metedata) { qDebug()


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有